<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>es5 原型链实现方法继承</title>
  <!-- 
  在 JavaScript ES5 中，原型链是实现方法继承的重要机制。通过原型链，子类可以访问父类的方法。
  1. 原型链的基本概念
  - 每个 JavaScript 对象都有一个原型（ `[[Prototype]]` ），可以通过  `Object.getPrototypeOf(obj)`  或  `obj.__proto__`  访问。
  - 通过原型链，子类实例可以访问父类的属性和方法。

  2. 方法继承的实现步骤
  在 ES5 中，通常有以下几种方式通过原型链实现方法继承：

   2.1 原型链继承
  通过将子类的原型指向父类的实例，使得子类可以访问父类的方法。
  function Parent() {
      this.parentProperty = "I am a parent property";
  }

  Parent.prototype.parentMethod = function() {
      console.log("This is a method from Parent");
  };

  function Child() {
      this.childProperty = "I am a child property";
  }

  // 继承 Parent
  Child.prototype = new Parent();
  Child.prototype.constructor = Child;

  var childInstance = new Child();
  childInstance.parentMethod(); // 输出: This is a method from Parent
  在这个例子中， `Child`  的原型指向  `Parent`  的实例，因此  `childInstance`  可以调用  `parentMethod()` 。

   2.2 组合继承
  组合继承结合了原型链继承和借用构造函数的优点，既能共享父类的方法，又能在子类中传递参数。
  function Parent(name) {
      this.name = name;
  }

  Parent.prototype.sayHello = function() {
      console.log("Hello from " + this.name);
  };

  function Child(name, age) {
      Parent.call(this, name); // 借用构造函数
      this.age = age;
  }

  // 继承原型
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;

  Child.prototype.sayAge = function() {
      console.log("I am " + this.age + " years old.");
  };

  var childInstance = new Child("Alice", 10);
  childInstance.sayHello(); // 输出: Hello from Alice
  childInstance.sayAge();   // 输出: I am 10 years old.
  在这个例子中， `Child`  通过借用构造函数获取  `Parent`  的属性，同时通过  `Object.create()`  继承了  `Parent`  的方法。

  3. 注意事项
  - 性能问题：过长的原型链可能导致性能下降，因为查找属性需要逐层查找。
  - 属性遮蔽：如果实例对象有与原型同名的属性，实例对象会优先使用自己的属性，这被称为“遮蔽”。
  - 构造函数的调用：在子类构造函数中调用父类构造函数，以确保子类实例能够正确初始化父类的属性。

  4. 总结
  通过原型链实现方法继承是 JavaScript ES5 中的重要特性。通过合理使用原型链，开发者可以实现代码的复用和继承。理解原型链的工作原理和继承的实现方式对于构建复杂的对象模型和编写高效的 JavaScript 代码至关重要。
  -->
</head>
<body>

  <script>
    // 定义构造类
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }

    // 定义方法
    Person.prototype.sayHello = function() {
      console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
    };

    Person.prototype.sayAge = function() {
      console.log("I am " + this.age + " years old.");
    };

    // 定义学生类
    function Student(name, age, height, address, sno, score) {
      this.name = name
      this.age = age
      this.height = height
      this.address = address

      this.sno = sno
      this.score = score
    }
    
    // 方式一: 父类的原型直接赋值给子类的原型
    // 缺点: 父类和子类共享通一个原型对象, 修改了任意一个, 另外一个也被修改
    // Person.prototype = Student.prototype

    // 方式二: 创建一个父类的实例对象(new Person()), 用这个实例对象来作为子类的原型对象
    // 创建实例
    var p = new Person("Alice", 25);
    Student.prototype = p

    Student.prototype.studying = function() {
      console.log("studying~")
    }

    var stu1 = new Student("kobe", 30, 111, 100)
    stu1.studying()
    stu1.sayHello()
    stu1.sayAge()

    console.log(stu1.__proto__ === Student.prototype);
    console.log(stu1.name, stu1.age)
    console.log(stu1)
    
    
  </script>
  
</body>
</html>